package com.intelligent.ispc.core.service.impl;

import com.google.common.collect.Lists;
import com.intelligent.ispc.common.mapper.BeanMapper;
import com.intelligent.ispc.common.persistence.DynamicSpecifications;
import com.intelligent.ispc.common.persistence.Hibernates;
import com.intelligent.ispc.common.persistence.SearchFilter;
import com.intelligent.ispc.common.utils.ApplicationException;
import com.intelligent.ispc.common.utils.ExceptionMessages;
import com.intelligent.ispc.core.dto.RoleDto;
import com.intelligent.ispc.core.entity.Resource;
import com.intelligent.ispc.core.entity.Role;
import com.intelligent.ispc.core.entity.User;
import com.intelligent.ispc.core.repository.jdbc.UserDao;
import com.intelligent.ispc.core.repository.jpa.RoleDao;
import com.intelligent.ispc.core.service.RoleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.*;
import java.util.List;
import java.util.Map;

/**
 * Created by dmrj on 2015/10/28.
 */
@Service
public class RoleServiceImpl implements RoleService {
    private static Logger logger = LoggerFactory.getLogger(RoleServiceImpl.class);

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private UserDao userDao;

    @Override
    public void saveRole(RoleDto roleDto) {
        if (roleDto == null || roleDto.getName() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Role role = roleDao.findByName(roleDto.getName());

        if (role != null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
        }

        role = new Role();
        BeanMapper.copy(roleDto, role);

        logger.debug("Saving user:{}", roleDto);
        roleDao.save(role);
        logger.info("Save done,role = {}", role.getName());
    }

    @Override
    public void updateRole(RoleDto roleDto) {
        if (roleDto == null || roleDto.getId() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Role role = new Role();
        BeanMapper.copy(roleDto, role);

        logger.debug("Updating role:{}", roleDto);
        roleDao.save(role);
        logger.info("Update done,role={}", role.getName());

    }

    @Override
    public void deleteRole(String uuid) {
        if (uuid == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Role role = roleDao.findById(Long.valueOf(uuid));
        if (role == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        for (User user : role.getUsers()) {
            user.getRoles().remove(role);
        }
        for (Resource resource : role.getResources()) {
            resource.getRoles().remove(role);
        }
        logger.debug("Deleting role:{}", role.getName());
        roleDao.delete(role);
        logger.info("Delete done, role = {}", role.getName());
    }

    @Override
    public RoleDto findById(String id) {
        if (id == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Role role = roleDao.findById(Long.valueOf(id));
        if (role == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        RoleDto roleDto = new RoleDto();
        BeanMapper.copy(role, roleDto);

        return roleDto;
    }

    @Override
    public RoleDto findByName(String name) {
        if (name == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Role role = roleDao.findByName(name);

        if (role == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        RoleDto roleDto = new RoleDto();
        BeanMapper.copy(role, roleDto);
        return roleDto;
    }

    @Override
    public List<RoleDto> findAllRoles() {
        List<Role> roleList = (List<Role>) roleDao.findAll();
        List<RoleDto> roleDtoList = Lists.newArrayList();

        for (Role role : roleList) {
            RoleDto roleDto = new RoleDto();
            BeanMapper.copy(role, roleDto);
            roleDtoList.add(roleDto);
        }

        return roleDtoList;
    }

    @Override
    public List<RoleDto> searchRole(Map<String, Object> searchParams) {
        logger.debug("Searching role with{},{}", searchParams.keySet(), searchParams.values());
        Map<String, SearchFilter> fliters = SearchFilter.parse(searchParams);
        Specification<Role> spec = DynamicSpecifications.bySearchFilter(fliters.values(), Role.class);

        List<Role> roleList = roleDao.findAll(spec);
        logger.debug("Searching role,total elements:{}", roleList.size());

        List<RoleDto> roleDtoList = Lists.newArrayList();
        for (Role role : roleList) {
            RoleDto roleDto = new RoleDto();
            Hibernates.initLazyProperty(role.getDescription());

            BeanMapper.copy(role, roleDto);
            roleDtoList.add(roleDto);
        }

        return roleDtoList;
    }

    @Override
    public Page<RoleDto> searchRole(Map<String, Object> searchParams, Pageable pageable) {
        logger.debug("Searching role with {},{}", searchParams.keySet(), searchParams.values(), pageable);

        Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);
        Specification<Role> spec = DynamicSpecifications.bySearchFilter(filters.values(), Role.class);

        Page<Role> rolePage = roleDao.findAll(spec, pageable);
        logger.debug("Searching user,total elements:{}", rolePage.getTotalElements());
        List<Role> roleList = rolePage.getContent();
        List<RoleDto> roleDtoList = Lists.newArrayList();
        for (Role role : roleList) {
            RoleDto roleDto = new RoleDto();
            Hibernates.initLazyProperty(role.getDescription());

            BeanMapper.copy(role, roleDto);
            roleDtoList.add(roleDto);
        }

        Page<RoleDto> pageRoleDto = new PageImpl<RoleDto>(roleDtoList, pageable, rolePage.getTotalElements());
        return pageRoleDto;
    }

    @Override
    public Page<RoleDto> searchRoleByDescription(String resourceName, Pageable pageable) {
        logger.debug("Searching user with description {}", resourceName, pageable);

        Specification<Role> spec = new Specification<Role>() {
            public Predicate toPredicate(Root<Role> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                Join<Role, Resource> resourceJoin = root.join(root.getModel().getSingularAttribute("resource", Resource.class), JoinType.LEFT);
                Predicate p1 = cb.equal(resourceJoin.get("resourceName").as(String.class), resourceName);
                query.where(p1);
                return query.getRestriction();
            }
        };

        Page<Role> rolePage = roleDao.findAll(spec, pageable);
        logger.debug("Searching user,total elements:{}", rolePage.getTotalElements());
        List<Role> roleList = rolePage.getContent();
        List<RoleDto> roleDtoList = Lists.newArrayList();
        for (Role role : roleList) {
            RoleDto roleDto = new RoleDto();
            Hibernates.initLazyProperty(role.getDescription());

            BeanMapper.copy(role, roleDto);
            roleDtoList.add(roleDto);
        }
        Page<RoleDto> roleDtoPage = new PageImpl<RoleDto>(roleDtoList, pageable, rolePage.getTotalElements());

        return roleDtoPage;
    }
}
